<!DOCTYPE html>
<html class="no-js" lang="zh">
  <head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="alternate" href="https://www.desgard.com" hreflang="pt-BR">
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#005344">
<title>Guardia · 瓜地</title>
<meta name="google-site-verification" content="zS1dSn20XtA4FJYEOQLXqI0boxZdMnJ2g3beje-cl20">
<meta name="description" content="I write many code to write less code.💻">
<meta name="keywords" content="">
<!-- Social: Facebook / Open Graph -->
<meta property="og:url" content="https://www.desgard.com/block2/">
<meta property="og:title" content="       浅谈 block（2） - 截获变量方式 | Gua  ">
<meta property="og:description" content="I write many code to write less code.💻">
<meta property="og:site_name" content="Desgard_Duan">
<meta property="og:locale" content="pt_BR">
<meta property="og:type" content="website">
<meta property="og:author" content="https://www.facebook.com/desgard.duan">
<meta property="og:image" content="https://www.desgard.com">
<!-- Social: Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@nandomoreirame">
<meta name="twitter:domain" content="https://www.desgard.com">
<meta name="twitter:title" content="       浅谈 block（2） - 截获变量方式 | Gua  ">
<meta name="twitter:description" content="I write many code to write less code.💻">
<meta name="twitter:image:src" content="https://www.desgard.com">
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="114x114" href="https://www.desgard.com/assets/ico/apple-touch-icon-114-516f4e19976b9e4dbb77ad9b576831fe.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://www.desgard.com/assets/ico/apple-touch-icon-72-5409b2df229305703caf583d86c845ab.png">
<link rel="apple-touch-icon" href="https://www.desgard.com/assets/ico/apple-touch-icon-57-aa873e019cf659e0d4e6a0b5bb9f379d.png">
<link rel="shortcut icon" href="https://www.desgard.com/assets/ico/favicon-4298be3d3fbe23e18d65bded9d930899.png">
<!-- rel prev and next -->
<!-- Canonical link tag -->
<link rel="canonical" href="https://www.desgard.com/block2/">
<link rel="alternate" type="application/rss+xml" title="Gua" href="https://www.desgard.com/feed.xml">
<link rel="stylesheet" href="/assets/main-0b7b828712f4c43b75bba4861c907bb1.css">
<script src="/assets/modernizr-custom-cb807611a3e262b2eac59444cbab74d6.js" data-cfasync="false"></script>
<script src="/assets/js/jquery-3.1.js"></script>
<script src="/assets/js/jquery.rotate.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Website",
  "publisher": "www.desgard.com",
  "url": "http://www.desgard.com/",
  "description": "瓜地"
}
</script>
<script type="text/javascript">
  var disqus_shortname = 'desgard',
      baseurl          = '';
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-52446115-1']);
_gaq.push(['_trackPageview']);
(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>-->
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?c5a8123bc51782a3083a631ed9ff50e4";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [ ['$','$'] ],
        displayMath: [ ['$$','$$'] ],
        processEscapes: true
      }
    });
  </script>
  </head>
  <body class="post-template single">
    <header class="header">
  <div class="container">
    <h1><a href="/"><strong>desgard</strong>.com</a></h1>
    <nav class="navbar">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="#!" data-modal="modalSearch"><i class="fa fa-search"></i></a></li>
        <li><a href="/comment " target="_blank"><i class="fa fa-comments"></i></a></li>
        <li><a href="/feed.xml" target="_blank"><i class="fa fa-feed"></i></a></li>
      </ul>
    </nav>
  </div>
</header>
    <main class="wrapper container" itemprop="mainContentOfPage" itemscope="itemscope" itemtype="http://schema.org/Blog">
      <article class="post" itemscope="itemscope" itemtype="http://schema.org/BlogPosting" itemprop="blogPost">
  <header class="post-header">
    <h1>浅谈 block（2） - 截获变量方式</h1>
    <div class="post-meta">
      <time datetime="2015-10-13">
        <i class="fa fa-calendar-o"></i> <time datetime="2016-09-03"> 2016-09-03
      </time>
      <span>
        <i class="fa fa-comments"></i> <a href="#container">Comment me!</a>
      </span>
      <span>
<!--
        <i class="fa fa-folder-open-o"></i> 
-->
      </span>
    </div>
  </header>
  <p>本文会通过 clang 的 <code>-rewrite-objc</code> 选项来分析 block 的 C 转换源代码。其分析方式在该系列上一篇有详细介绍。请先阅读 <em><a href="https://desgard.com/block1/">浅谈 block（1） - clang 改写后的 block 结构</a></em> 。</p>
<h2 id="section">截获自动变量</h2>
<p>首先需要做代码准备工作，我们编写一段 block 引用外部变量的 c 代码。</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/7E32C4CC-DE35-469E-8EC1-C20BCAE4CD0C.png" alt="7E32C4CC-DE35-469E-8EC1-C20BCAE4CD0" /></p>
<p>编译运行成功后，使用 <code>-rewrite-objc</code> 进行改写。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">clang</span> <span class="o">-</span><span class="n">rewrite</span><span class="o">-</span><span class="n">objc</span> <span class="n">block</span><span class="o">.</span><span class="n">c</span></code></pre></div>
<p>简化代码后，得到以下主要代码：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">__main_block_impl_0</span> <span class="p">{</span>
	<span class="n">struct</span> <span class="n">__block_impl</span> <span class="n">impl</span><span class="p">;</span>
	<span class="n">struct</span> <span class="n">__main_block_desc_0</span><span class="o">*</span> <span class="no">Desc</span><span class="p">;</span>
	<span class="n">char</span> <span class="o">*</span><span class="n">str</span><span class="p">;</span>
	<span class="n">__main_block_impl_0</span><span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="n">fp</span><span class="p">,</span> <span class="n">struct</span> <span class="n">__main_block_desc_0</span> <span class="o">*</span><span class="n">desc</span><span class="p">,</span> <span class="n">char</span> <span class="o">*</span><span class="n">_str</span><span class="p">,</span> <span class="n">int</span> <span class="n">flags</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">:</span> <span class="n">str</span><span class="p">(</span><span class="n">_str</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">isa</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">_NSConcreteStackBlock</span><span class="p">;</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">flags</span><span class="p">;</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">FuncPtr</span> <span class="o">=</span> <span class="n">fp</span><span class="p">;</span>
		<span class="no">Desc</span> <span class="o">=</span> <span class="n">desc</span><span class="p">;</span>
	<span class="p">}</span>                                                                                                                                                                                                      
<span class="p">};</span>
<span class="n">static</span> <span class="n">void</span> <span class="n">__main_block_func_0</span><span class="p">(</span><span class="n">struct</span> <span class="n">__main_block_impl_0</span> <span class="o">*</span><span class="n">__cself</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">char</span> <span class="o">*</span><span class="n">str</span> <span class="o">=</span> <span class="n">__cself</span><span class="o">-&gt;</span><span class="n">str</span><span class="p">;</span> <span class="sr">//</span> <span class="n">bound</span> <span class="n">by</span> <span class="n">copy</span>
	<span class="nb">printf</span><span class="p">(</span><span class="s2">&quot;%s</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">str</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">static</span> <span class="n">struct</span> <span class="n">__main_block_desc_0</span> <span class="p">{</span>
	<span class="n">size_t</span> <span class="n">reserved</span><span class="p">;</span>
	<span class="n">size_t</span> <span class="no">Block_size</span><span class="p">;</span>
<span class="p">}</span> <span class="n">__main_block_desc_0_DATA</span> <span class="o">=</span> <span class="p">{</span> 
	<span class="mi">0</span><span class="p">,</span> 
	<span class="n">sizeof</span><span class="p">(</span><span class="n">struct</span> <span class="n">__main_block_impl_0</span><span class="p">)</span>
<span class="p">};</span>
<span class="n">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">char</span> <span class="o">*</span><span class="n">str</span> <span class="o">=</span> <span class="s2">&quot;Desgard_Duan&quot;</span><span class="p">;</span>
	<span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="n">block</span><span class="p">)()</span> <span class="o">=</span> <span class="p">((</span><span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="o">&amp;</span><span class="n">__main_block_impl_0</span><span class="p">((</span><span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="n">__main_block_func_0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__main_block_desc_0_DATA</span><span class="p">,</span> <span class="n">str</span><span class="p">));</span>
	<span class="p">((</span><span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="n">__block_impl</span> <span class="o">*</span><span class="p">))((</span><span class="n">__block_impl</span> <span class="o">*</span><span class="p">)</span><span class="n">block</span><span class="p">)</span><span class="o">-&gt;</span><span class="no">FuncPtr</span><span class="p">)((</span><span class="n">__block_impl</span> <span class="o">*</span><span class="p">)</span><span class="n">block</span><span class="p">);</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>与上一篇转换的源码不同的是，block 语法表达中的变量作为成员添加到了 <code>__main_block_func_0</code> 结构体中。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">__main_block_impl_0</span> <span class="p">{</span>
	<span class="n">struct</span> <span class="n">__block_impl</span> <span class="n">impl</span><span class="p">;</span>
	<span class="n">struct</span> <span class="n">__main_block_desc_0</span><span class="o">*</span> <span class="no">Desc</span><span class="p">;</span>
	<span class="n">char</span> <span class="o">*</span><span class="n">str</span><span class="p">;</span> <span class="sr">//</span> <span class="err">外部引用变量</span>
<span class="p">}</span></code></pre></div>
<p>并且，在该结构体中的应用变量类型与外部的类型完全相同。在初始化该结构体实例的构造函数也自然会有所差异：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="n">block</span><span class="p">)()</span> <span class="o">=</span> <span class="p">((</span><span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="o">&amp;</span><span class="n">__main_block_impl_0</span><span class="p">((</span><span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="n">__main_block_func_0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__main_block_desc_0_DATA</span><span class="p">,</span> <span class="n">str</span><span class="p">));</span></code></pre></div>
<p>去掉强转语法简化代码：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="n">block</span><span class="p">)()</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">__main_block_impl_0</span><span class="p">(</span><span class="n">__main_block_func_0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__main_block_desc_0_DATA</span><span class="p">,</span> <span class="n">str</span><span class="p">);</span></code></pre></div>
<p>在构造时，除了要传递自身(self) <code>__main_block_func_0</code> 结构体，而且还要传递 block 的基本信息，即 reserved 和 size 。这里传递了一个全局结构体对象 <code>__main_block_desc_0_DATA</code> ，因为他是为 block 量身设计的。最后在将引用值参数传入构造函数中，以便于构造带外部引用参数的 block。</p>
<p>进入构造函数后，发现了含有冒号表达的构造语法：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">__main_block_impl_0</span><span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="n">fp</span><span class="p">,</span> <span class="n">struct</span> <span class="n">__main_block_desc_0</span> <span class="o">*</span><span class="n">desc</span><span class="p">,</span> <span class="n">char</span> <span class="o">*</span><span class="n">_str</span><span class="p">,</span> <span class="n">int</span> <span class="n">flags</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">:</span> <span class="n">str</span><span class="p">(</span><span class="n">_str</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">impl</span><span class="o">.</span><span class="n">isa</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">_NSConcreteStackBlock</span><span class="p">;</span>
	<span class="n">impl</span><span class="o">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">flags</span><span class="p">;</span>
	<span class="n">impl</span><span class="o">.</span><span class="n">FuncPtr</span> <span class="o">=</span> <span class="n">fp</span><span class="p">;</span>
	<span class="no">Desc</span> <span class="o">=</span> <span class="n">desc</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>其实，冒号表达式是 C++ 中的一个固有语法。这是显示构造的方法之一。另外还有一种构造显示构造方式，其语法较为繁琐，即使用 this 指针构造。（关于 C++ 构造函数，可以学习 msdn 文档 <em><a href="https://msdn.microsoft.com/zh-cn/library/s16xw1a8.aspx">构造函数 (C++)</a></em> ）</p>
<p>之后的代码与前一篇分析相同，不再讨论。</p>
<p>通过整个构造 block 流程分析，我们发现当 block 引用外部对象时，会在结构体内部新建立一个成员进行存储。此处我们使用的是 char* 类型，而在结构体中所使用的 char* 是结构体的成员，所以可以得知：<strong>block 引用外部对象时候，不是简单的指针引用（浅复制），而是一种重建（深复制）方式</strong>（括号内外分别对于基本数据类型和对象分别描述）<strong>）</strong>。所以如果在 block 中对外部对象进行修改，无论是值修改还是指针修改，自然是没有任何效果。</p>
<h2 id="block-">引入 __block 关键字对截取变量一探究竟</h2>
<p>上文中的 block 所引用的外部成员是一个字符型指针，当我们在 block 内部对其修改后，很容易的想到，会改变该指针的指向。而当 block 中引用外部变量为常用数据类型会有些许的不同：</p>
<p>我们来看这个例子 (这是来自 <em>Pro multithreading and memory management for iOS and OS X</em> 2.3.3 一节的例子)：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">void</span> <span class="p">(</span><span class="o">^</span><span class="n">blk</span><span class="p">)(</span><span class="n">void</span><span class="p">)</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span><span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">};</span></code></pre></div>
<p>执行代码后会报 error ：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="ss">error</span><span class="p">:</span> <span class="n">variable</span> <span class="n">is</span> <span class="ow">not</span> <span class="n">assignable</span> <span class="p">(</span><span class="n">missing</span> <span class="n">__block</span> <span class="n">type</span> <span class="n">specifier</span><span class="p">)</span>
    <span class="n">void</span> <span class="p">(</span><span class="o">^</span><span class="n">blk</span><span class="p">)(</span><span class="n">void</span><span class="p">)</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span><span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">};</span></code></pre></div>
<p>上述书中对此情况是这样解释的：</p>
<blockquote>
  <p>block 中所使用的被截获自动变量如同“带有自动变量值的匿名函数”，仅截获自动变量的值。 block 中使用自动变量后，在 block 的结构体实力中重写该自动变量也不会改变原先截获的自动变量。</p>
</blockquote>
<p>这应该是 clang 对 block 的引用外界局部值做的保护措施，也是为了维护 C 语言中的作用域特性。既然谈到了作用域，那么是否可以使用显示声明存储域类型从而在 block 中修改该变量呢？答案是可以的。当 block 中截取的变量为静态变量（static），使用下例进行试验：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">static</span> <span class="n">int</span> <span class="n">static_val</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
	<span class="n">void</span> <span class="p">(</span><span class="o">^</span><span class="n">blk</span><span class="p">)(</span><span class="n">void</span><span class="p">)</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span>
		<span class="n">static_val</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
	<span class="p">};</span>
<span class="p">}</span></code></pre></div>
<p>装换后的代码：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">__main_block_impl_0</span> <span class="p">{</span>
	<span class="n">struct</span> <span class="n">__block_impl</span> <span class="n">impl</span><span class="p">;</span>
	<span class="n">struct</span> <span class="n">__main_block_desc_0</span><span class="o">*</span> <span class="no">Desc</span><span class="p">;</span>
	<span class="n">int</span> <span class="o">*</span><span class="n">static_val</span><span class="p">;</span>
	<span class="n">__main_block_impl_0</span><span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="n">fp</span><span class="p">,</span> <span class="n">struct</span> <span class="n">__main_block_desc_0</span> <span class="o">*</span><span class="n">desc</span><span class="p">,</span> <span class="n">int</span> <span class="o">*</span><span class="n">_static_val</span><span class="p">,</span> <span class="n">int</span> <span class="n">flags</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">isa</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">_NSConcreteStackBlock</span><span class="p">;</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">flags</span><span class="p">;</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">FuncPtr</span> <span class="o">=</span> <span class="n">fp</span><span class="p">;</span>
		<span class="no">Desc</span> <span class="o">=</span> <span class="n">desc</span><span class="p">;</span>
	<span class="p">}</span>
<span class="p">};</span>
<span class="n">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">static</span> <span class="n">int</span> <span class="n">static_val</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
	<span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="n">blk</span><span class="p">)(</span><span class="n">void</span><span class="p">)</span> <span class="o">=</span> <span class="p">((</span><span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="o">&amp;</span><span class="n">__main_block_impl_0</span><span class="p">((</span><span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="n">__main_block_func_0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__main_</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
 <span class="p">}</span></code></pre></div>
<p>会发现在构造函数中使用的静态指针 <code>int *_static_val</code> 对其进行访问。将静态变量 <code>static_val</code> 的指针传递给 <code>__main_block_impl_0</code> 结构体的构造函数并加以保存。通过指针进行作用域拓展，是 C 中很常见的思想及做法，也是超出作用域使用变量的最简单方法。</p>
<p>那么我们为什么在引用自动变量的时候，不使用该自动变量的指针呢？是应为在 block 截获变量后，原来的自动变量已经废弃，因此block 中超过变量作用域从而无法通过指针访问原来的自动变量。</p>
<p>为了解决这个问题，其实在 block 扩展中已经提供了方法（<a href="http://clang.llvm.org/docs/BlockLanguageSpec.html#the-block-storage-qualifier">官方文档</a>）。即使用 <code>__block</code> 关键字。</p>
<p><code>__block</code> 关键字更准确的表达应为 <em>__block说明符(__block storage-class-specifier)</em> ，用来描述存储域。在 C 语言中已经存有如下存储域声明关键字：</p>
<ul>
  <li>typedef：常用在为数据类型起别名，而不是一般认识的存储域声明关键字作用。但在归类上属于存储域声明关键字。</li>
  <li>extern：限制标示，限制定义变量在所有模块中作为全局变量，并只能被定义一次。</li>
  <li>static：静态变量存储在 .data 区。</li>
  <li>auto：自动变量存储在栈中。</li>
  <li>register：约束变量为单值，存储在CPU寄存器内。</li>
</ul>
<p><code>__block</code> 关键字类似于 <code>static</code>、<code>auto</code>、<code>register</code>，用于将变量存于指定存储域。来分析一下在变量声明前增加 <code>__block</code> 关键字后 clang 对于 block 的转换动作。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">__block</span> <span class="n">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">void</span> <span class="p">(</span><span class="o">^</span><span class="n">blk</span><span class="p">)(</span><span class="n">void</span><span class="p">)</span> <span class="o">=</span> <span class="o">^</span> <span class="p">{</span>
   <span class="n">val</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">};</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="err">要点</span> <span class="mi">1</span><span class="err">：</span><span class="n">__block</span> <span class="err">变量转换结构</span>
<span class="n">struct</span> <span class="n">__Block_byref_val_0</span> <span class="p">{</span>
	<span class="n">void</span> <span class="o">*</span><span class="n">__isa</span><span class="p">;</span>
	<span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="n">__forwarding</span><span class="p">;</span>
	<span class="n">int</span> <span class="n">__flags</span><span class="p">;</span>
	<span class="n">int</span> <span class="n">__size</span><span class="p">;</span>
	<span class="n">int</span> <span class="n">val</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">struct</span> <span class="n">__main_block_impl_0</span> <span class="p">{</span>
	<span class="n">struct</span> <span class="n">__block_impl</span> <span class="n">impl</span><span class="p">;</span>
	<span class="n">struct</span> <span class="n">__main_block_desc_0</span><span class="o">*</span> <span class="no">Desc</span><span class="p">;</span>
	<span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="n">val</span><span class="p">;</span> <span class="sr">//</span> <span class="n">by</span> <span class="n">ref</span>
	<span class="n">__main_block_impl_0</span><span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="n">fp</span><span class="p">,</span> <span class="n">struct</span> <span class="n">__main_block_desc_0</span> <span class="o">*</span><span class="n">desc</span><span class="p">,</span> <span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="n">_val</span><span class="p">,</span> <span class="n">int</span> <span class="n">flags</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">:</span> <span class="n">val</span><span class="p">(</span><span class="n">_val</span><span class="o">-&gt;</span><span class="n">__forwarding</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">isa</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">_NSConcreteStackBlock</span><span class="p">;</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">flags</span><span class="p">;</span>
		<span class="n">impl</span><span class="o">.</span><span class="n">FuncPtr</span> <span class="o">=</span> <span class="n">fp</span><span class="p">;</span>
		<span class="no">Desc</span> <span class="o">=</span> <span class="n">desc</span><span class="p">;</span>
	<span class="p">}</span>
<span class="p">};</span>
<span class="n">static</span> <span class="n">void</span> <span class="n">__main_block_func_0</span><span class="p">(</span><span class="n">struct</span> <span class="n">__main_block_impl_0</span> <span class="o">*</span><span class="n">__cself</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="n">val</span> <span class="o">=</span> <span class="n">__cself</span><span class="o">-&gt;</span><span class="n">val</span><span class="p">;</span> <span class="sr">//</span> <span class="n">bound</span> <span class="n">by</span> <span class="n">ref</span>
	<span class="sr">//</span> <span class="err">要点</span> <span class="mi">2</span><span class="err">：</span><span class="n">__forwarding</span> <span class="err">自环指针存在意义</span>
	 <span class="p">(</span><span class="n">val</span><span class="o">-&gt;</span><span class="n">__forwarding</span><span class="o">-&gt;</span><span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">static</span> <span class="n">struct</span> <span class="n">__main_block_desc_0</span> <span class="p">{</span>
	<span class="n">size_t</span> <span class="n">reserved</span><span class="p">;</span>	
	<span class="n">size_t</span> <span class="no">Block_size</span><span class="p">;</span>
	<span class="sr">//</span> <span class="err">要点</span> <span class="mi">3</span><span class="err">：</span><span class="n">copy</span><span class="o">/</span><span class="n">dispose</span> <span class="err">方法内部实现</span>
	<span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="n">copy</span><span class="p">)(</span><span class="n">struct</span> <span class="n">__main_block_impl_0</span><span class="o">*</span><span class="p">,</span> <span class="n">struct</span> <span class="n">__main_block_impl_0</span><span class="o">*</span><span class="p">);</span>
	<span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="n">dispose</span><span class="p">)(</span><span class="n">struct</span> <span class="n">__main_block_impl_0</span><span class="o">*</span><span class="p">);</span>
<span class="p">}</span> <span class="n">__main_block_desc_0_DATA</span> <span class="o">=</span> <span class="p">{</span> 
	<span class="mi">0</span><span class="p">,</span> 
	<span class="n">sizeof</span><span class="p">(</span><span class="n">struct</span> <span class="n">__main_block_impl_0</span><span class="p">),</span> 
	<span class="n">__main_block_copy_0</span><span class="p">,</span> 
	<span class="n">__main_block_dispose_0</span>
	<span class="p">};</span>
<span class="n">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">__attribute__</span><span class="p">((</span><span class="n">__blocks__</span><span class="p">(</span><span class="n">byref</span><span class="p">)))</span> <span class="n">__Block_byref_val_0</span> <span class="n">val</span> <span class="o">=</span> <span class="p">{</span>
    	<span class="p">(</span><span class="n">void</span><span class="o">*</span><span class="p">)</span><span class="mi">0</span><span class="p">,</span>
	    <span class="p">(</span><span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> 
	    <span class="mi">0</span><span class="p">,</span> 
	    <span class="n">sizeof</span><span class="p">(</span><span class="n">__Block_byref_val_0</span><span class="p">),</span> <span class="mi">1</span>
    <span class="p">};</span>
    <span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="n">blk</span><span class="p">)(</span><span class="n">void</span><span class="p">)</span> <span class="o">=</span> <span class="p">((</span><span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="o">&amp;</span><span class="n">__main_block_impl_0</span><span class="p">((</span><span class="n">void</span> <span class="o">*</span><span class="p">)</span><span class="n">__main_block_func_0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__main_block_desc_0_DATA</span><span class="p">,</span> <span class="p">(</span><span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> <span class="mi">570425344</span><span class="p">));</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>发现核心代码部分有所增加，我们先从入口函数看起。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">__Block_byref_val_0</span> <span class="n">val</span> <span class="o">=</span> <span class="p">{</span>
	<span class="p">(</span><span class="n">void</span><span class="o">*</span><span class="p">)</span><span class="mi">0</span><span class="p">,</span>
    <span class="p">(</span><span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> 
    <span class="mi">0</span><span class="p">,</span> 
    <span class="n">sizeof</span><span class="p">(</span><span class="n">__Block_byref_val_0</span><span class="p">),</span> 
    <span class="mi">1</span>
<span class="p">};</span></code></pre></div>
<p>原先的 val 变成了 <code>__Block_byre_val_0</code> 结构体类型变量。并且这个结构体的定义是之前未曾见过的。并且我们将 val 初始化的数值 1，也出现在这个构造中，说明该结构体持有原成员变量。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">__Block_byref_val_0</span> <span class="p">{</span>
	<span class="n">void</span> <span class="o">*</span><span class="n">__isa</span><span class="p">;</span>
	<span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="n">__forwarding</span><span class="p">;</span>
	<span class="n">int</span> <span class="n">__flags</span><span class="p">;</span>
	<span class="n">int</span> <span class="n">__size</span><span class="p">;</span>
	<span class="n">int</span> <span class="n">val</span><span class="p">;</span>
<span class="p">};</span></code></pre></div>
<p>在 <code>__block</code> 变量的结构体中，除了有指向类对象的 <code>isa</code> 指针，对象负载信息 <code>flags</code>，大小 <code>size</code>，以及持有的原变量 <code>val</code>，还有一个自身类型的 <code>__forwarding</code> 指针。从构造函数中，会发现一个有趣的现象，<strong><code>__forwarding</code> 指针会指向自身，形成自环</strong>。后面会详细介绍它。</p>
<p>而在 block 体执行段，是这样定义的。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">static</span> <span class="n">void</span> <span class="n">__main_block_func_0</span><span class="p">(</span><span class="n">struct</span> <span class="n">__main_block_impl_0</span> <span class="o">*</span><span class="n">__cself</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">__Block_byref_val_0</span> <span class="o">*</span><span class="n">val</span> <span class="o">=</span> <span class="n">__cself</span><span class="o">-&gt;</span><span class="n">val</span><span class="p">;</span> <span class="sr">//</span> <span class="n">bound</span> <span class="n">by</span> <span class="n">ref</span>
	 <span class="p">(</span><span class="n">val</span><span class="o">-&gt;</span><span class="n">__forwarding</span><span class="o">-&gt;</span><span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>第一步中获得 val 的方法和 block 中引用外部变量的方式是一致的，通过 self 来获取变量。而对于外部 __block 变量赋值的时候，这种写法引起了我们的注意：<code>(val-&gt;__forwarding-&gt;val) = 2;</code> ，这样做的目的何在，在后文会做出分析。</p>
<h2 id="block--1">__block 变量结构</h2>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/__block%E7%BB%93%E6%9E%84.png" alt="__block结构" /></p>
<p>当 block 内部引用外部的 __block 变量，会使用以上结构对 __block 做出转换。另外，该结构体并不声明在 <code>__main_block_impl_0</code> block 结构体中，是因为这样可以对多个 block 引用 __block 情况下，达到复用效果，从而节省不必要的空间开销。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">__block</span> <span class="n">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">void</span> <span class="p">(</span><span class="o">^</span><span class="n">blk1</span><span class="p">)(</span><span class="n">void</span><span class="p">)</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span><span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;};</span>
<span class="n">void</span> <span class="p">(</span><span class="o">^</span><span class="n">blk2</span><span class="p">)(</span><span class="n">void</span><span class="p">)</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span><span class="n">val</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;};</span></code></pre></div>
<p>只观察入口方法：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">__Block_byref_val_0</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">__Block_byref_val_0</span><span class="p">),</span> <span class="mi">10</span><span class="p">};</span>
<span class="n">blk1</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">__main_block_impl_0</span><span class="p">(</span><span class="n">__main_block_func_0</span>
									<span class="p">,</span> <span class="o">&amp;</span><span class="n">__main_block_desc_0_DATA</span>
									<span class="p">,</span> <span class="o">&amp;</span><span class="n">val</span>
									<span class="p">,</span> <span class="mh">0x22000000</span><span class="p">);</span>
<span class="n">blk2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">__main_block_impl_0</span><span class="p">(</span><span class="n">__main_block_func_1</span>
									<span class="p">,</span> <span class="o">&amp;</span><span class="n">__main_block_desc_1_DATA</span>
									<span class="p">,</span> <span class="o">&amp;</span><span class="n">val</span>
									<span class="p">,</span> <span class="mh">0x22000000</span><span class="p">);</span></code></pre></div>
<p>发现 val 指针被复用，使得两个 block 同时使用一个 __block 只需要对其结构声明一次即可。</p>
<h2 id="objective-c--block">接触 Objective-C 语言环境下的 block</h2>
<p>通过两篇文的 block 的结构转换，我们发现其实 block 的实质是一个<em>对象 (Object)</em>，从封装成结构体对象，再到 isa 指针结构，都是明显的体现。对于 __block 也是如此，在转换后将其封装成了 __block 结构体类型，以对象方式处理。</p>
<p>带着 C 代码中的 block 扩展转换规则开始进入 Objective-C block 的学习。首先需要知道 block 的三个类型。</p>
<table>
  <thead>
    <tr>
      <th style="text-align: left">类型</th>
      <th style="text-align: right">对象存储域</th>
      <th>地址单元</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left">_NSConcreteStackBlock</td>
      <td style="text-align: right">栈</td>
      <td>高地址</td>
    </tr>
    <tr>
      <td style="text-align: left">_NSConcreteMallocBlock</td>
      <td style="text-align: right">堆</td>
      <td> </td>
    </tr>
    <tr>
      <td style="text-align: left">_NSConcreteGloalBlock</td>
      <td style="text-align: right">静态区(.data)</td>
      <td>低地址</td>
    </tr>
  </tbody>
</table>
<p>在上一篇文中的末尾部分，简单的说了一下全局静态的存储问题。这里再一次强调， <code>_NSConcreteGloalBlock</code> 的 block 会在一下两种情况下出现（与 clang 转换结果不大相同）：</p>
<ul>
  <li>全局变量位置</li>
  <li>block 中不引用外部变量</li>
</ul>
<p>而在其他情况下，基本上 block 的类型都为 _NSConcreteStackBlock 。但是在栈上的 block 会受到作用域的限制，一旦所属的变量作用域结束，该 block 就会被释放。由此，引出了 _NSConcreteMallocBlock 堆 block 类型。</p>
<p>block 提供了将 block 和 __block 变量从栈上复制到堆上的方法来解决这个问题。将配置在站上的 block 复制到堆上，这样可以保证在 block 变量作用域结束后，堆上仍旧可访问。</p>
<p>__block 变量通过 __forwarding 可以无论在堆上还是栈上都能正常访问。当 block 存储在堆上的时候，对应的栈上 block 的 __forwarding 成员会断开自环，而指向堆上的 block 对象。这也就是 __forwarding 指针存在的真实用意。</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/block_forwarding.png" alt="block_forwarding" /></p>
<p>在复制到堆的过程中，__forwarding 指针是如何更改指向的？这个问题在下一篇中进行介绍。这篇文主要讲述了 __block 变量在 block 中的结构，以及如何获取外部变量，并可以对其修改的详细过程，希望有所收获。</p>
  <footer class="post-footer">
    <section class="author">
      <h4>Desgard_Duan</h4>
      <p>I write many code to write less code.💻</p>
    </section>
<aside class="share">
  <h4>Share this.</h4>
  <a href="http://twitter.com/share?text=《浅谈 block（2） - 截获变量方式》 -- Guardia · 瓜地&amp;url=https://www.desgard.com/block2/"
  onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-twitter-square"></i>
  </a>
  <a href="http://v.t.sina.com.cn/share/share.php?url=https://www.desgard.com/block2/&amp;title=《浅谈 block（2） - 截获变量方式》 —— Guardia · 瓜地" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-weibo"></i>
  </a>
</aside>
      <hr>
<aside id="comments" class="disqus">
  <div id="container"></div>
  <!-- <link rel="stylesheet" href="/assets/css/gitment.css">
  <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
  <script>
  var gitment = new Gitment({
    id: "https://www.desgard.com/block2/", 
    owner: 'Desgard',
    repo: 'desgard.github.com',
    oauth: {
      client_id: 'e2612df42f3f2a83e71c',
      client_secret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
    },
  })
  gitment.render('container')
  </script> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
<div id='gitalk-container'></div>
<script>
    const gitalk = new Gitalk({
        id: "https://www.desgard.com/block2/", 
        clientID: 'e2612df42f3f2a83e71c',
        clientSecret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
        repo: 'desgard.github.com',
        owner: 'Desgard',
        admin: ['Desgard'],
        // facebook-like distraction free mode
        distractionFreeMode: false
    })
    gitalk.render('gitalk-container')
</script>
</aside>
  </footer>
</article>
    </main>
<footer class="footer">
  <div class="container">
    <ul class="icons">
      <li>
        <a href="https://github.com/desgard" class="icon-github" target="_blank">
          <i class="fa fa-github"></i>
        </a>
      </li>
      <li>
        <a href="https://www.facebook.com/desgard.duan" class="icon-facebook" target="_blank">
          <i class="fa fa-facebook"></i>
        </a>
      </li>
      <li>
        <a href="https://twitter.com/Desgard_Duan" class="icon-twitter" target="_blank">
          <i class="fa fa-twitter"></i>
        </a>
      </li>
      <li>
        <a href="https://stackoverflow.com/users/6119149/desgard-duan" class="icon-github" target="_blank">
          <i class="fa fa-stack-overflow"></i>
        </a>
      </li>
      <li>
        <a href="https://weibo.com/desgard" class="icon-instagram" target="_blank">
          <i class="fa fa-weibo"></i>
        </a>
      </li>
    </ul>
    <p>
      <q>I write many code to write less code.💻</q>
      <small>– Gua</small>
    </p>
    <small class="clearfix">
      Powered by <a href="http://jekyllrb.com" target="_blank">Jekyll</a> • <a href="https://github.com/desgard" target="_blank">Open source <i class="fa fa-heart"></i></a>
    </small>
  </div>
</footer>
<a class="scroll-up fa fa-chevron-up bounce" href="#" data-position="0"></a>
<div id="modalSearch" class="modal">
  <div class="modal__overlay"></div>
  <div class="modal__content">
    <a href="#!" class="modal-close" data-modal-close>&times;</a>
    <div class="search-container">
      <input type="text" id="search-input" placeholder="Search articles">
      <ul id="results-container"></ul>
    </div>
  </div>
</div>
    <script src="/assets/main-52d417e8a6ff9f5b168386d37c96338a.js"></script>
  </body>
  <script>
    var link = "" ;
    var os = function() {  
      var ua = navigator.userAgent,  
      isWindowsPhone = /(?:Windows Phone)/.test(ua),  
      isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,   
      isAndroid = /(?:Android)/.test(ua),   
      isFireFox = /(?:Firefox)/.test(ua),   
      isChrome = /(?:Chrome|CriOS)/.test(ua),  
      isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),  
      isPhone = /(?:iPhone)/.test(ua) && !isTablet,  
      isPc = !isPhone && !isAndroid && !isSymbian;  
      return {  
        isTablet: isTablet,  
        isPhone: isPhone,  
        isAndroid : isAndroid,  
        isPc : isPc  
      };  
    }();  
    if (link.length > 0) {
      if (os.isAndroid || os.isPhone) {
        location.replace(link);
      }
    }
  </script>
</html>